﻿#include "pch.h"
#if _QG_USE_D11
#include "qgdrv_d11_rdh.h"
#include "qgdrv_d11_cnv.h"
#include "qg_math.h"
#include "qgp/qgutypes.h"
#include "extend/sbvalues.h"

//////////////////////////////////////////////////////////////////////////
// D11
KIMPL_SUBCLASS(D11Rdh, "RenderDeviceD11");

namespace _qgp
{
	// 타입
	kType RdmTypeD11()
	{
		return D11Rdh::Type();
	}

	//
	static kModule* ModuleD3D11 = NULL;
	static kModule*	ModuleD3DCOMPILER = NULL;

	//
	struct D11Vertex2D : public qbVertexOrtho
	{
		void Set(float x, float y, float u, float v, kuint cc)
		{
			pos.Set(x, y);
			coord.Set(u, v);
			color = cc;
		}

		void Set(float x, float y, const tpl::Vec2& d, kuint cc)
		{
			pos.Set(x, y);
			coord = d;
			color = cc;
		}
	};

	//
	struct D11Vertex3D : public qbVertexPosTexColor
	{
		void Set(const tpl::Vec3& v, const tpl::Vec2& d, kuint cc)
		{
			pos = v;
			coord = d;
			color = cc;
		}

		void Set(const tpl::Vec3& v, kuint cc)
		{
			pos = v;
			coord.Set(0.0f, 0.0f);
			color = cc;
		}
	};
}

//
PFN_D3D11_CREATE_DEVICE D11Rdh::D3D11CREATEDEVICE = NULL;

pD3DCompile D11Rdh::D3DCOMPILE = NULL;
D11Rdh::pD3DReflect D11Rdh::D3DREFLECT = NULL;

//
static kModule*	TmpDll = NULL;
static kfunc TmpFunc = NULL;

//
void D11Rdh::TypeInitialize(kType type)
{
	_qgp::ModuleD3D11 = k_mod_open("d3d11", NULL, 0);
	if (_qgp::ModuleD3D11)
		k_mod_sym_func(_qgp::ModuleD3D11, "D3D11CreateDevice", (kfunc*)&D3D11CREATEDEVICE);

	TmpDll = k_mod_open("tmpdll", NULL, 0);
	if (TmpDll)
		k_mod_sym_func(TmpDll, "TMPDLL", &TmpFunc);

	_qgp::ModuleD3DCOMPILER = k_mod_open(D3DCOMPILER_DLL_A, NULL, 0);
	if (_qgp::ModuleD3DCOMPILER)
	{
		k_mod_sym_func(_qgp::ModuleD3DCOMPILER, "D3DCompile", (kfunc*)&D3DCOMPILE);
		k_mod_sym_func(_qgp::ModuleD3DCOMPILER, "D3DReflect", (kfunc*)&D3DREFLECT);
	}
}

//
void D11Rdh::TypeFinalize(kType type)
{
	if (_qgp::ModuleD3DCOMPILER)
		k_mod_close(_qgp::ModuleD3DCOMPILER);

	if (TmpDll)
		k_mod_close(TmpDll);

	if (_qgp::ModuleD3D11)
		k_mod_close(_qgp::ModuleD3D11);
}

//
D11Rdh::D11Rdh()
{

}

//
D11Rdh::~D11Rdh()
{
	//
	_Finalize();

	//
	if (_psc)
	{
		_psc->Dispose();
		delete _psc;
	}

	//
	_context = NULL;
	_device = NULL;
}

//
bool D11Rdh::_Initialize(qgStub* stub, kint flags)
{
	if (_qgp::ModuleD3D11 == NULL ||
		_qgp::ModuleD3DCOMPILER == NULL)
	{
		// DLL 로드할 수 없음
		return false;
	}

	//
	tpl::Point size = stub->GetSize();

	// 초기화 자료
	static const D3D_DRIVER_TYPE c_drvtype[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	static const D3D_FEATURE_LEVEL c_ftlevel[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
		D3D_FEATURE_LEVEL_9_3,
		D3D_FEATURE_LEVEL_9_2,
		D3D_FEATURE_LEVEL_9_1
	};

	// D11 디바이스
	HRESULT hr;
	UINT dflg = 0;
	ksize_t n;

#if _SB_DEBUG_
	if (k_sisd_ossub() < KSIPFS_WINDOWS_10)
	{
		// [2014-10-6 ksh] 윈10에서 SDK가 없으면 D11디버깅이 안되더라
		dflg |= D3D11_CREATE_DEVICE_DEBUG;
	}
#endif

	for (n = 0; n < K_COUNTOF(c_drvtype); n++)
	{
		hr = D3D11CREATEDEVICE(NULL, c_drvtype[n], NULL, dflg,
			c_ftlevel, (UINT)K_COUNTOF(c_ftlevel), D3D11_SDK_VERSION,
			_device.InitPtr(), &_feature_level, _context.InitPtr());

		if (SUCCEEDED(hr))
		{
			_driver_type = c_drvtype[n];
			break;
		}
	}

	if (FAILED(hr))
		return false;

	// 개체 만들기
	IDXGIDevice* dxgi = NULL;
	IDXGIAdapter* adapter = NULL;
	IDXGIFactory* factory = NULL;

	if (FAILED(hr = _device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi)) ||
		FAILED(hr = dxgi->GetParent(__uuidof(IDXGIAdapter), (void**)&adapter)) ||
		FAILED(hr = adapter->GetParent(__uuidof(IDXGIFactory), (void**)&factory)))
	{
		// 헐 머임..
		kunk_release(factory);
		kunk_release(adapter);
		kunk_release(dxgi);

		return false;
	}

	// 디스플레이 모드
	_EnumDisplayMode(adapter);

	// 세이더 프로필 + 버전 별 기능 할당
	if (_feature_level == D3D_FEATURE_LEVEL_11_0 ||
		_feature_level == D3D_FEATURE_LEVEL_11_1 ||
		_feature_level > D3D_FEATURE_LEVEL_11_1)
	{
		k_strcpy(_extp.prof_vs, "vs_5_0");
		k_strcpy(_extp.prof_ps, "ps_5_0");
		k_strcpy(_extp.prof_gs, "gs_5_0");

		_caps.version_shader = 0x0500;
		_caps.max_texture_dimension = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}
	else if (_feature_level == D3D_FEATURE_LEVEL_10_1)
	{
		k_strcpy(_extp.prof_vs, "vs_4_1");
		k_strcpy(_extp.prof_ps, "ps_4_1");
		k_strcpy(_extp.prof_gs, "gs_4_1");

		_caps.version_shader = 0x0400;
		_caps.max_texture_dimension = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}
	else if (_feature_level >= D3D_FEATURE_LEVEL_10_0)
	{
		k_strcpy(_extp.prof_vs, "vs_4_0");
		k_strcpy(_extp.prof_ps, "ps_4_0");
		k_strcpy(_extp.prof_gs, "gs_4_0");

		_caps.version_shader = 0x0400;
		_caps.max_texture_dimension = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}
	else if (_feature_level == D3D_FEATURE_LEVEL_9_3)
	{
		k_strcpy(_extp.prof_vs, "vs_4_0_level_9_3");
		k_strcpy(_extp.prof_ps, "ps_4_0_level_9_3");
		_extp.prof_gs[0] = '\0';

		_caps.version_shader = 0x0300;
		_caps.max_texture_dimension = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}
	else
	{
		k_strcpy(_extp.prof_vs, "vs_4_0_level_9_1");
		k_strcpy(_extp.prof_ps, "ps_4_0_level_9_1");
		_extp.prof_gs[0] = '\0';

		_caps.version_shader = 0x0200;
		_caps.max_texture_dimension = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION;
	}

	// CAPABILITIES. D11은 기능이 고정이라 그냥 정의함
	// 혹시 caps 관련 기능이 발견되면 수정여부 있음
	k_strcpy(_caps.name, "D11");
	k_strcpy(_caps.vendor, "Microsoft");

	_caps.version = D3D11_SDK_VERSION;

	_caps.supp_occlusion = true;

	_caps.max_texture_count = D11MaxTextureUnitCount();		// 원래 지원: 128
	_caps.max_offtex_count = D11MaxOffTextureCount();		// 원래 지원: 8

	_caps.texture_image_flag = QGRESF_BGRA;

	// 멀티 샘플링
	if (!K_OFLAG(flags, QGSDF_MSAA))
	{
		_msaa.Count = 1;
		_msaa.Quality = 0;
	}
	else
	{
		hr = _device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, 4, &_msaa.Quality);

		if (FAILED(hr))
		{
			// D11 스펙상 4X MSAA는 되야 정상. 안되는게 이상한 거임
			_msaa.Count = 1;
			_msaa.Quality = 0;
		}
		else
		{
			_msaa.Count = 4;
			_msaa.Quality--;
		}
	}

	// 스왑체인
	_psc = new D11SwapChain();
	hr = _psc->Create(factory, _device, _msaa, size, (HWND)stub->GetHandle(), K_OFLAG(flags, QGSDF_FULL) == 0);

	// 윗쪽 인터페이스 삭제
	factory->Release();
	adapter->Release();
	dxgi->Release();

	// 스왑체인 실패
	if (FAILED(hr))
		return false;

	// 기본 뷰포트 설정
	_psc->Render(_context);

	// 정보 표시
	if (!_scrs.adt.empty())
	{
		const qbAdapterDesc& adt = _scrs.adt[0];
		kobj_traces(KLV_INFO, "DirectX11 Driver");
		kobj_tracef(KLV_INFO, " - Driver: %s", adt.desc);
		kobj_tracef(KLV_INFO, " - Dedicate video memory size is %lld", adt.memsize);
		k_strncpy(_caps.vendor, adt.desc, 63);
	}

	// 보관
	_keep.rdos.Initialize(D11Rasz::Type());
	_keep.shaders.Initialize(D11Shader::Type());
	_keep.vertexlayouts.Initialize(D11Layout::Type());
	_keep.depthbuffers.Initialize(D11DepthBuffer::Type());
	_keep.texture2s.Initialize(D11Texture2::Type());
	_keep.texture3s.Initialize(D11Texture3::Type());
	_keep.animtextures.Initialize(D11AnimTexture::Type());
	_keep.buffers.Initialize();
	_keep.dispose = false;

	// 슈퍼
	return super_type::_Initialize(stub, flags);
}

//
bool D11Rdh::_EnumDisplayMode(IDXGIAdapter* adapter)
{
	HRESULT hr;

	// 스크린 모드
	IDXGIOutput* output;
	if (FAILED(hr = adapter->EnumOutputs(0, &output)))
		return false;

	UINT cnt = 0;

	if (FAILED(hr = output->GetDisplayModeList(D11SURFACEFORMAT, DXGI_ENUM_MODES_INTERLACED, &cnt, NULL)))
	{
		output->Release();
		return false;
	}

	DXGI_MODE_DESC* lst = k_new(cnt, DXGI_MODE_DESC);
	hr = output->GetDisplayModeList(D11SURFACEFORMAT, DXGI_ENUM_MODES_INTERLACED, &cnt, lst);

	output->Release();

	if (FAILED(hr))
	{
		k_delete(lst);
		return false;
	}

	for (UINT i = 0; i < cnt; i++)
	{
		if (lst[i].RefreshRate.Denominator == 0)
			continue;

		qbScreenDesc scr;
		scr.dstn = 0;
		scr.width = lst[i].Width;
		scr.height = lst[i].Height;
		scr.freq = (kint)k_rint((float)lst[i].RefreshRate.Numerator / (float)lst[i].RefreshRate.Denominator);
		scr.internal_fmt = lst[i].Format;
		scr.internal_type = 0;
		_scrs.scr.push_back(scr);
	}

	k_delete(lst);

	// 어댑터...는 하나 밖에
	DXGI_ADAPTER_DESC adc;

	if (FAILED(hr = adapter->GetDesc(&adc)))
		return false;

	qbAdapterDesc adt;
	adt.index = 0;
	adt.memsize = (klong)adc.DedicatedVideoMemory;
	k_conv_utf16_to_utf8(adt.desc, K_MAX_PATH - 1, adc.Description, 0);
	_scrs.adt.push_back(adt);

	// 소트
	if (!_scrs.scr.empty())
	{
		std::sort(_scrs.scr.begin(), _scrs.scr.end(),
			[ ](const qbScreenDesc& l, const qbScreenDesc& r) throw() -> bool
		{
			return
				//l.freq > r.freq && 
				l.width > r.width ||
				l.height > r.height;
		});
	}

	return true;
}

// 리셋 또는 준비
bool D11Rdh::_ResetPrepare(const tpl::Point& size, bool prepare)
{
	// 기본값
	super_type::_ResetPrepare(size, prepare);

	// 내부 TM
	_prsn.t_otm = tpl::Mat4::OrthoLH((float)size.width, (float)size.height, 0.0f, 1.0f);

	// 캐시 초기화 (값을 넣지 않는 것은 어짜피 널 값이 기본이기 때문에)
	_cache.tpg = (qgTopology)K_INVALID_VALUE;

	// 인터페이스 준비
	if (prepare)
	{
		// 색깔 출력용 텍스쳐
		_extp.tx_white = CreateTexture2(QGCF32_BGRA, 16, 16, 1, QGRESF_DYNAMIC | QGTEXF_NOMIP, "d11_default_texture_white");

		qbTextureMap tm;
		_extp.tx_white->Map(&tm, QGENTF_WRITE);
		memset(tm.data, 0xFF, tm.pitch * tm.height);
		_extp.tx_white->Unmap();

		// 버퍼
		kint dbsizes[D11DynBuffer::MAXBUFFER] = {128, 1024, 64 * 1024, 256 * 1024, 512 * 1024, 1024 * 1024};
		_dib = new D11DynBuffer(this, QGBUFFER_INDEX, dbsizes);
		_dvb = new D11DynBuffer(this, QGBUFFER_VERTEX, dbsizes);

		// 세이더
		static const char s_vs_ortho[] = \
			"cbuffer ShaderConstants : register(b0)\n" \
			"{\n" \
			"	float4x4 sOrthoProj : packoffset(c0);\n" \
			"};\n" \
			"struct VSINPUT \n" \
			"{\n" \
			"	float4 pos : POSITION;\n" \
			"	float4 color : COLOR;\n" \
			"};\n" \
			"struct VSOUTPUT \n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"VSOUTPUT VS(VSINPUT i) \n" \
			"{\n" \
			"	VSOUTPUT o;\n" \
			"	o.pos = mul(sOrthoProj, float4(i.pos.xy, 0.0f, 1.0f));\n" \
			"	o.color = i.color;\n" \
			"	o.coord = i.pos.zw;\n" \
			"	return o;\n" \
			"}\n";
		static const char s_vs_proj[] = \
			"cbuffer ShaderConstants : register(b0)\n" \
			"{\n" \
			"	float4x4 sWorldViewProj : packoffset(c0);\n" \
			"};\n" \
			"struct VSINPUT \n" \
			"{\n" \
			"	float3 pos : POSITION;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"	float4 color : COLOR;\n" \
			"};\n" \
			"struct VSOUTPUT \n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"VSOUTPUT VS(VSINPUT i) \n" \
			"{\n" \
			"	VSOUTPUT o;\n" \
			"	o.pos = mul(float4(i.pos, 1.0f), sWorldViewProj);\n" \
			"	o.color = i.color;\n" \
			"	o.coord = i.coord;\n" \
			"	return o;\n" \
			"}\n";
		static const char s_ps_tex[] = \
			"SamplerState splr0 : register(s0);\n" \
			"Texture2D<float4> tex0 : register(t0);\n" \
			"struct PSINPUT\n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"float4 PS(PSINPUT i) : SV_Target\n" \
			"{\n" \
			"	float4 t = tex0.Sample(splr0, i.coord);\n" \
			"	float4 c = t * i.color;\n" \
			"	if (c.a == 0.0f) discard;\n" \
			"	return saturate(c);\n"\
			"}\n";
		static const char s_ps_glyph[] = \
			"SamplerState splr0 : register(s0);\n" \
			"Texture2D<float> tex0 : register(t0);\n" \
			"struct PSINPUT\n" \
			"{\n" \
			"	float4 pos : SV_Position;\n" \
			"	float4 color : COLOR;\n" \
			"	float2 coord : TEXCOORD;\n" \
			"};\n" \
			"float4 PS(PSINPUT i) : SV_Target\n" \
			"{\n" \
			"	float t = tex0.Sample(splr0, i.coord);\n" \
			"	if (t == 0.0f) discard;\n" \
			"	return (t * i.color.a) * float4(i.color.rgb, 1.0f);\n"\
			"}\n";

		// 그리기용 세이더
		_extp.sp_ortho = CreateShader("d11_sp_ortho");
		_extp.sp_ortho->BindData(QGSHADER_VS, s_vs_ortho, sizeof(s_vs_ortho), 0);
		_extp.sp_ortho->BindData(QGSHADER_PS, s_ps_tex, sizeof(s_ps_tex), 0);
		_extp.sp_ortho->Link();

		_extp.sp_proj = CreateShader("d11_sp_proj");
		_extp.sp_proj->BindData(QGSHADER_VS, s_vs_proj, sizeof(s_vs_proj), 0);
		_extp.sp_proj->BindData(QGSHADER_PS, s_ps_tex, sizeof(s_ps_tex), 0);
		_extp.sp_proj->Link();

		_extp.sp_glyph = CreateShader("d11_sp_glyph");
		_extp.sp_glyph->BindData(QGSHADER_VS, s_vs_ortho, sizeof(s_vs_ortho), 0);
		_extp.sp_glyph->BindData(QGSHADER_PS, s_ps_glyph, sizeof(s_ps_glyph), 0);
		_extp.sp_glyph->Link();

		// 레이아웃
		static qbLayoutElement s_le_p4_color[2] =
		{
			{QGLOU_POSITION, 0, QGLOT_FLOAT4, 0, 0, },
			{QGLOU_COLOR, 0, QGLOT_COLOR, 0, 0, },
		};
		static qbLayoutElement s_le_p3_t2_color[3] =
		{
			{QGLOU_POSITION, 0, QGLOT_FLOAT3, 0, 0, },
			{QGLOU_TEXTURE, 0, QGLOT_FLOAT2, 0, 0, },
			{QGLOU_COLOR, 0, QGLOT_COLOR, 0, 0, },
		};
		_extp.vl_p4_color = CreateLayout(K_COUNTOF(s_le_p4_color), s_le_p4_color);
		_extp.vl_p3_t2_color = CreateLayout(K_COUNTOF(s_le_p3_t2_color), s_le_p3_t2_color);
	}

	// 렌더타겟...

	return true;
}

//
void D11Rdh::_Finalize()
{
	// 기본 오브젝트 제거

	// 텍스쳐
	kobj_unload(_extp.tx_white);

	// 버퍼
	if (_dib != NULL) delete _dib;
	if (_dvb != NULL) delete _dvb;

	// 레이아웃
	kobj_unload(_extp.vl_p4_color);
	kobj_unload(_extp.vl_p3_t2_color);

	// 세이더
	kobj_unload(_extp.sp_ortho);
	kobj_unload(_extp.sp_proj);
	kobj_unload(_extp.sp_glyph);

	// 보관 제거
	_keep.dispose = true;
	_keep.buffers.Dispose();
	_keep.animtextures.Dispose();
	_keep.texture3s.Dispose();
	_keep.texture2s.Dispose();
	_keep.depthbuffers.Dispose();
	_keep.vertexlayouts.Dispose();
	_keep.shaders.Dispose();
	_keep.rdos.Dispose();

	//
	super_type::_Finalize();
}

//
bool D11Rdh::Layout(kint width, kint height)
{
	if (!super_type::Layout(width, height))
		return false;

	kGuardSpinLock(_lock)
	{
		if (FAILED(_psc->Layout(_device, _msaa, tpl::Point(width, height))))
		{
			// 스왑체인 레이아웃 실패!
			// 오류 메시지는 어카지!
		}

		// 기본 뷰포트 설정
		_psc->Render(_context);

		// 내부 TM
		_prsn.t_otm = tpl::Mat4::OrthoLH((float)width, (float)height, 0.0f, 1.0f);
	}

	return true;
}

//
bool D11Rdh::Enter(kint clearance)
{
	super_type::Enter(clearance);

	if (clearance & QGCLEAR_RENDER)
		_context->ClearRenderTargetView(_psc->GetRenderTargetView(), _prsn.cr_bg.ToPointer());

	if (clearance & (QGCLEAR_DEPTH | QGCLEAR_STENCIL))
	{
		UINT u = clearance & (D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL);
		_context->ClearDepthStencilView(_psc->GetDepthStencilView(), u, 1.0f, 0);
	}

	return true;
}

//
bool D11Rdh::Leave()
{
	return super_type::Leave();
}

//
bool D11Rdh::Flush()
{
	super_type::Flush();
	return _psc->Flush();
}

// 뷰포트
void D11Rdh::SetViewport(kint x, kint y, kint w, kint h, float minz, float maxz)
{
	super_type::SetViewport(x, y, w, h, minz, maxz);

	D3D11_VIEWPORT vp;
	vp.TopLeftX = (float)x;
	vp.TopLeftY = (float)y;
	vp.Width = (float)w;
	vp.Height = (float)h;
	vp.MinDepth = minz;
	vp.MaxDepth = maxz;

	if (vp.Width > 0 && vp.Height > 0)
	{
		_prsn.t_otm = tpl::Mat4::OrthoLH((float)vp.Width, (float)vp.Height, 0.0f, 1.0f);
		_context->RSSetViewports(1, &vp);
	}
}

// 시자스
void D11Rdh::SetScissor(bool enable, const tpl::Rect& area)
{
	RDHNFO_INVOKE();

	D3D11_RECT rt;

	if (enable)
	{
		rt.left = area.left;
		rt.top = area.top;
		rt.right = area.right;
		rt.bottom = area.bottom;
	}
	else
	{
		rt.left = 0;
		rt.top = _caps.max_texture_dimension;
		rt.right = 0;
		rt.bottom = _caps.max_texture_dimension;
	}

	_context->RSSetScissorRects(1, &rt);
}

// 뎁스 스텐실 상태
void D11Rdh::SetDhsl(qgDhsl* ptr)
{
	RDHNFO_INVOKE();
	RDHNFO_STATE();

	if (ptr == NULL)
	{
		if (_cache.dhsl != NULL)
		{
			_context->OMSetDepthStencilState(NULL, 0);
			_cache.dhsl = NULL;
		}
	}
	else
	{
		ID3D11DepthStencilState* dhsl = ptr->CastDescription<ID3D11DepthStencilState>();
		if (_cache.dhsl != dhsl)
		{
			_context->OMSetDepthStencilState(dhsl, 0);
			_cache.dhsl = dhsl;
		}
	}
}

// 래스터라이저 설정
void D11Rdh::SetRasz(qgRasz* ptr)
{
	RDHNFO_INVOKE();
	RDHNFO_STATE();

	if (ptr == NULL)
	{
		if (_cache.rasz != NULL)
		{
			_context->RSSetState(NULL);
			_cache.rasz = NULL;
		}
	}
	else
	{
		ID3D11RasterizerState* rasz = ptr->CastDescription<ID3D11RasterizerState>();
		if (_cache.rasz != rasz)
		{
			_context->RSSetState(rasz);
			_cache.rasz = rasz;
		}
	}
}

// 블렌드 상태
void D11Rdh::SetBlnd(qgBlnd* ptr)
{
	RDHNFO_INVOKE();
	RDHNFO_STATE();

	if (ptr == NULL)
	{
		if (_cache.blnd != NULL)
		{
			_context->OMSetBlendState(NULL, NULL, 0xFFFFFFFF);
			_cache.blnd = NULL;
		}
	}
	else
	{
		ID3D11BlendState* blnd = ptr->CastDescription<ID3D11BlendState>();
		if (_cache.blnd != blnd)
		{
			_context->OMSetBlendState(blnd, NULL, 0xFFFFFFFF);
			_cache.blnd = blnd;
		}
	}
}

// 샘플러 상태
void D11Rdh::SetSplr(kint stage, qgSplr* ptr)
{
	RDHNFO_INVOKE();
	RDHNFO_STATE();

	k_return_if_fail((ksize_t)stage < D11MaxTextureUnitCount());

	if (ptr == NULL)
	{
		if (_cache.splr[stage] != NULL)
		{
			ID3D11SamplerState* splr = qbStaticSplr<>::GetPtr()->CastDescription<ID3D11SamplerState>();
			_context->PSSetSamplers(stage, 1, &splr);
			_cache.splr[stage] = NULL;
		}
	}
	else
	{
		ID3D11SamplerState* splr = ptr->CastDescription<ID3D11SamplerState>();
		if (_cache.splr[stage] != splr)
		{
			_context->PSSetSamplers(stage, 1, &splr);
			_cache.splr[stage] = splr;
		}
	}
}

// 세이더
void D11Rdh::SetShader(qgShader* shd, qgVertexLayout* lo)
{
	RDHNFO_INVOKE();
	RDHNFO_SHADER();

	// 세이더
	if (_cache.shd_ptr != shd)
	{
		kobj_unload(_cache.shd_ptr);

		if (shd == NULL)
		{
			if (_cache.shd_vs != NULL)
			{
				_context->VSSetShader(NULL, NULL, 0);
				_cache.shd_vs = NULL;
			}

			if (_cache.shd_ps != NULL)
			{
				_context->PSSetShader(NULL, NULL, 0);
				_cache.shd_ps = NULL;
			}

			_cache.shd_ptr = NULL;
		}
		else
		{
			ID3D11VertexShader* vs = ((D11Shader*)shd)->_vs;
			ID3D11PixelShader* ps = ((D11Shader*)shd)->_ps;

			if (_cache.shd_vs != vs)
			{
				_context->VSSetShader(vs, NULL, 0);

				_cache.shd_vs = vs;
			}

			if (_cache.shd_ps != ps)
			{
				_context->PSSetShader(ps, NULL, 0);

				_cache.shd_ps = ps;
			}

			_cache.shd_ptr = (D11Shader*)shd;
			shd->Load();
		}
	}

	// 레이아웃
	if (lo == NULL)
	{
		if (_cache.ilos != NULL)
		{
			_context->IASetInputLayout(NULL);
			_cache.ilos = NULL;
		}
	}
	else
	{
		ID3D11InputLayout* ilos = lo->CastDescription<ID3D11InputLayout>();
		if (ilos == NULL)
		{
			// 아직 입력 레이아웃이 만들어지지 않았음. 여기서 만들기
			D11Layout* dl = (D11Layout*)lo;
			if (!dl->_BakeShader(_cache.shd_ptr, &ilos, this))
			{
				// 오류가 발생했으면 BakeShader안에서 처리함
				return;
			}
		}

		if (_cache.ilos != ilos)
		{
			_context->IASetInputLayout(ilos);
			_cache.ilos = ilos;
		}
	}
}

//
void D11Rdh::_InternalSetTopology(qgTopology tpg)
{
	if (_cache.tpg != tpg)
	{
		_context->IASetPrimitiveTopology(D11Convert::Topology(tpg));
		_cache.tpg = tpg;
	}
}

//
void D11Rdh::_InternalSetBufferVertex(kint stage, D11Buffer* buf, UINT stride, UINT offset, bool check)
{
	if (check)
	{
		if (buf == NULL)
		{
			if (_cache.vb[stage] != NULL)
			{
				//_context->IASetVertexBuffers(stage, 1, NULL, NULL, NULL);	// 이거 되나?
				_cache.vb[stage] = NULL;
			}
		}
		else
		{
			ID3D11Buffer* pb = buf->CastDescription<ID3D11Buffer>();
			if (_cache.vb[stage] != pb)
			{
				_context->IASetVertexBuffers(stage, 1, &pb, &stride, &offset);
				_cache.vb[stage] = pb;
			}
		}
	}
	else
	{
		ID3D11Buffer* pb = buf->CastDescription<ID3D11Buffer>();
		if (_cache.vb[stage] != pb)
		{
			_context->IASetVertexBuffers(stage, 1, &pb, &stride, &offset);
			_cache.vb[stage] = pb;
		}
	}
}

//
void D11Rdh::_InternalSetBufferIndex(D11Buffer* buf, DXGI_FORMAT fmt, UINT offset, bool check)
{
	if (check)
	{
		if (buf == NULL)
		{
			if (_cache.ib != NULL)
			{
				//_context->IASetIndexBuffer(NULL, 0, 0);	// 이거 되나?
				_cache.ib = NULL;
			}
		}
		else
		{
			ID3D11Buffer* pb = buf->CastDescription<ID3D11Buffer>();
			if (_cache.ib != pb)
			{
				_context->IASetIndexBuffer(pb, fmt, offset);
				_cache.ib = pb;
			}
		}
	}
	else
	{
		ID3D11Buffer* pb = buf->CastDescription<ID3D11Buffer>();
		if (_cache.ib != pb)
		{
			_context->IASetIndexBuffer(pb, fmt, offset);
			_cache.ib = pb;
		}
	}
}

//
void D11Rdh::SetBufferVertex(kint stage, qgBuffer* buf)
{
	RDHNFO_INVOKE();

	k_return_if_fail((ksize_t)stage < QGSGVERT_MAX_VALUE);
	k_return_if_fail(buf != NULL);

	_InternalSetBufferVertex(stage, (D11Buffer*)buf, buf->GetStride(), 0, true);
}

//
void D11Rdh::SetBufferIndex(qgBuffer* buf)
{
	RDHNFO_INVOKE();

	k_return_if_fail(buf);

	DXGI_FORMAT fmt = D11Convert::IndexStrideFormat(buf->GetStride());
	_InternalSetBufferIndex((D11Buffer*)buf, fmt, 0, true);
}

//
void D11Rdh::SetTexture(kint stage, qgTexture* ptr)
{
	RDHNFO_INVOKE();
	RDHNFO_TEXTURE();

	k_return_if_fail(stage < QGSGTEX_MAX_VALUE);

	if (ptr == NULL)
	{
		if (_cache.srvs[stage] != NULL)
		{
			_cache.srvs[stage] = NULL;
			_context->PSSetShaderResources(stage, 1, &_cache.srvs[stage]);
		}
	}
	else
	{
		ID3D11ShaderResourceView* srv = ptr->CastDescription<ID3D11ShaderResourceView>();
		if (_cache.srvs[stage] != srv)
		{
			_context->PSSetShaderResources(stage, 1, &srv);
			_cache.srvs[stage] = srv;
		}
	}
}

//
void D11Rdh::SetRenderTargets(kint count, qgTexture** texs)
{
	RDHNFO_INVOKE();
	RDHNFO_RENDERER();

	if (count > QGSGOFF_MAX_VALUE)
		count = QGSGOFF_MAX_VALUE;

	if (count <= 0 || texs == NULL)
	{
		// 화면 그리기 모드
		for (kint i = 0; i < _cache.off_cnt; i++)
			kobj_reset(&_cache.off_texs[i]);
		_cache.off_cnt = 0;

		_psc->Render(_context);
		SetViewport(0, 0, _prsn.t_sdm.width, _prsn.t_sdm.height, 0.0f, 1.0f);
	}
	else
	{
		D11DepthBuffer* db = NULL;
		ID3D11RenderTargetView* rtvs[QGSGOFF_MAX_VALUE];
		bool istarget = count != _cache.off_cnt;

		for (kint i = 0; i < count; i++)
		{
			D11OffTexture* co = texs[i]->IsOff() ? (D11OffTexture*)texs[i] : NULL;
			if (co == NULL)
			{
				if (_cache.off_texs[i] == NULL)
					rtvs[i] = NULL;
				else
				{
					// 기존꺼 계속 씀
					co = _cache.off_texs[i];
					rtvs[i] = co->_rtv;
				}
			}
			else
			{
				if (db == NULL)
				{
					db = co->_db;
					if (db == NULL)
					{
						db = (D11DepthBuffer*)CreateDepthBuffer(co->_width, co->_height);
						co->_db = db;
					}
				}
		
				rtvs[i] = co->_rtv;
				
				if (co == _cache.off_texs[i])
				{
					// 이전 상태와 같음
				}
				else
				{
					kobj_unload(_cache.off_texs[i]);
					_cache.off_texs[i] = co;
					co->Load();
				
					if (!istarget)
						istarget = true;
				}
			}
		}

		if (istarget)
		{
			ID3D11DepthStencilView* dsv = dsv = db ? db->CastDescription<ID3D11DepthStencilView>() : NULL;
			_context->OMSetRenderTargets(count, rtvs, dsv);
			_cache.off_cnt = count;
			if (_cache.dsv != dsv)
				_cache.dsv = dsv;
		}

		//
		if (texs[0] != NULL)
			SetViewport(0, 0, texs[0]->GetWidth(), texs[0]->GetHeight(), 0.0f, 1.0f);
		else if (db != NULL)
			SetViewport(0, 0, db->GetWidth(), db->GetHeight(), 0.0f, 1.0f);
	}
}

//
void D11Rdh::ClearMRTs(kint clearance, kint colorcount, const tpl::ColorValue* colors, float depth, kuint stencil)
{
	RDHNFO_INVOKE();

	if (K_OFLAG(clearance, QGCLEAR_RENDER))
	{
		kint count = _cache.off_cnt;

		for (kint i = 0; i < count; i++)
		{
			D11OffTexture* off = (D11OffTexture*)_cache.off_texs[i];

			if (off != NULL)
			{
				ID3D11RenderTargetView* rtv = off->_rtv;
				tpl::Color cc = (colorcount>i && colors != NULL) ? tpl::Color(colors[i]) : _prsn.cr_bg;

				_context->ClearRenderTargetView(rtv, cc.ToPointer());
			}
		}
	}

	if (_cache.dsv != NULL && K_OFLAG(clearance, QGCLEAR_DEPTH | QGCLEAR_STENCIL))
	{
		UINT u = clearance&(D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL);

		_context->ClearDepthStencilView(_cache.dsv, u, depth, (UINT8)stencil);
	}
}

//
void D11Rdh::Draw(qgTopology tpg, kint verts)
{
	RDHNFO_INVOKE();
	RDHNFO_DRAWCALL();
	RDHNFO_VERTICES(verts);

	if (_cache.shd_ptr == NULL || _cache.vb[0] == NULL || _cache.ilos == NULL)
	{
		// 세이더 또는 꼭지점 또는 레이아웃 없이 그릴 순 없음
		kobj_tracep(KLV_ERROR, KERR_ARGUMENT);
	}
	else
	{
		// 그리기
		_InternalSetTopology(tpg);
		_CommitShader();
		_context->Draw(verts, 0);
	}
}

//
void D11Rdh::DrawIndexed(qgTopology tpg, kint indices)
{
	RDHNFO_INVOKE();
	RDHNFO_DRAWCALL();
	RDHNFO_VERTICES(indices);

	if (_cache.shd_ptr == NULL || _cache.vb[0] == NULL || _cache.ib == NULL || _cache.ilos == NULL)
	{
		// 세이더 또는 꼭지점 또는 인덱스 또는 레이아웃 없이 그릴 순 없음
		kobj_tracep(KLV_ERROR, KERR_ARGUMENT);
	}
	else
	{
		// 그리기
		_InternalSetTopology(tpg);
		_CommitShader();
		_context->DrawIndexed(indices, 0, 0);
	}
}

//
void D11Rdh::DrawSprite(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex, const tpl::Quad* coord)
{
	RDHNFO_INVOKE();

	k_return_if_ok(color.a < 10);

	if (!coord)
		coord = (const tpl::Quad*)&_math::Global::TexCoord;

	//
	kuint bgra = color.ToBgra();
	tpl::Quad rt = bound.Center(_prsn.t_vp.size.width, _prsn.t_vp.size.height).ToQuad();

	//
	SetDhsl(qbStaticDhsl<false>::GetPtr());
	SetRasz(qbStaticRasz<QGFILL_SOLID, QGCULL_BACK>::GetPtr());
	SetBlnd(qbStaticBlnd<QGCM_RGBA, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV>::GetPtr());
	SetSplr(0, qbStaticSplr<>::GetPtr());
	SetTexture(0, tex != NULL ? tex : _extp.tx_white);
	SetShader(_extp.sp_ortho, _extp.vl_p4_color);

	//
	_qgp::D11Vertex2D* vx;
	PrimitiveBegin(QGTPG_TRI_STRIP, 4, sizeof(_qgp::D11Vertex2D), (kpointer*)&vx);
	vx[0].Set(rt.left, rt.top, coord->left, coord->top, bgra);
	vx[1].Set(rt.right, rt.top, coord->right, coord->top, bgra);
	vx[2].Set(rt.left, rt.bottom, coord->left, coord->bottom, bgra);
	vx[3].Set(rt.right, rt.bottom, coord->right, coord->bottom, bgra);
	PrimitiveEnd();
}

//
void D11Rdh::DrawLines(bool ortho, bool strip, kint count, const tpl::Vec3* pts, const tpl::ColorValue* colors)
{
	RDHNFO_INVOKE();

	k_return_if_fail(count >= 2);
	k_return_if_fail(pts);

	//
	SetBlnd(qbStaticBlnd<QGCM_RGBA, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV>::GetPtr());
	SetSplr(0, qbStaticSplr<>::GetPtr());
	SetTexture(0, _extp.tx_white);

	//
	float x, y;
	kint i;

	if (ortho)
	{
		// 2D
		SetDhsl(qbStaticDhsl<false>::GetPtr());
		SetRasz(qbStaticRasz<QGFILL_SOLID, QGCULL_BACK>::GetPtr());
		SetShader(_extp.sp_ortho, _extp.vl_p4_color);

		tpl::Vec2 h(_prsn.t_vp.size.width / 2 - 0.5f, _prsn.t_vp.size.height / 2 - 0.5f);

		_qgp::D11Vertex2D* v2;

		PrimitiveBegin(
			strip ? QGTPG_LINE_STRIP : QGTPG_LINE_LIST,
			count, sizeof(_qgp::D11Vertex2D), (kpointer*)&v2);

		if (colors)
		{
			for (i = 0; i < count; i++)
			{
				x = -h.width + pts[i].x;
				y = h.height - pts[i].y;
				v2[i].Set(x, y, 0.0f, 0.0f, colors[i].ToBgra());
			}
		}
		else
		{
			for (i = 0; i < count; i++)
			{
				x = -h.width + (float)pts[i].x;
				y = h.height - (float)pts[i].y;
				v2[i].Set(x, y, 0.0f, 0.0f, 0xFFFFFFFF);
			}
		}

		PrimitiveEnd();
	}
	else
	{
		// 3D
		SetDhsl(qbStaticDhsl<true>::GetPtr());
		SetRasz(qbStaticRasz<QGFILL_SOLID, QGCULL_NONE>::GetPtr());
		SetShader(_extp.sp_proj, _extp.vl_p3_t2_color);

		_qgp::D11Vertex3D* v3;

		PrimitiveBegin(
			strip ? QGTPG_LINE_STRIP : QGTPG_LINE_LIST,
			count, sizeof(_qgp::D11Vertex3D), (kpointer*)&v3);

		if (colors)
		{
			for (i = 0; i < count; i++)
				v3[i].Set(pts[i], colors[i].ToBgra());
		}
		else
		{
			for (i = 0; i < count; i++)
				v3[i].Set(pts[i], 0xFFFFFFFF);
		}

		PrimitiveEnd();
	}
}

//
void D11Rdh::DrawStrips(bool ortho, kint count, const tpl::Vec3* pts, const tpl::ColorValue* colors, qgTexture* tex, const tpl::Vec2* coords)
{
	RDHNFO_INVOKE();

	k_return_if_fail(count >= 2);
	k_return_if_fail(pts);

	//
	SetBlnd(qbStaticBlnd<QGCM_RGBA, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV>::GetPtr());
	SetSplr(0, qbStaticSplr<>::GetPtr());
	SetTexture(0, tex != NULL ? tex : _extp.tx_white);

	//
	float x, y;
	kint i;

	if (ortho)
	{
		// 2D
		SetDhsl(qbStaticDhsl<false>::GetPtr());
		SetRasz(qbStaticRasz<QGFILL_SOLID, QGCULL_BACK>::GetPtr());
		SetShader(_extp.sp_ortho, _extp.vl_p4_color);

		tpl::Vec2 h(_prsn.t_vp.size.width / 2 - 0.5f, _prsn.t_vp.size.height / 2 - 0.5f);

		_qgp::D11Vertex2D* vx;
		PrimitiveBegin(QGTPG_LINE_STRIP, count + 1, sizeof(_qgp::D11Vertex2D), (kpointer*)&vx);

		if (colors)
		{
			if (coords)
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + pts[i].x;
					y = h.height - pts[i].y;
					vx[i].Set(x, y, coords[i], colors[i].ToBgra());
				}
			}
			else
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + pts[i].x;
					y = h.height - pts[i].y;
					vx[i].Set(x, y, 0.0f, 0.0f, colors[i].ToBgra());
				}
			}
		}
		else
		{
			if (coords)
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + pts[i].x;
					y = h.height - pts[i].y;
					vx[i].Set(x, y, coords[i], 0xFFFFFFFF);
				}
			}
			else
			{
				for (i = 0; i < count; i++)
				{
					x = -h.width + pts[i].x;
					y = h.height - pts[i].y;
					vx[i].Set(x, y, 0.0f, 0.0f, 0xFFFFFFFF);
				}
			}
		}

		PrimitiveEnd();
	}
	else
	{
		// 3D
		SetDhsl(qbStaticDhsl<true>::GetPtr());
		SetRasz(qbStaticRasz<QGFILL_SOLID, QGCULL_NONE>::GetPtr());
		SetShader(_extp.sp_proj, _extp.vl_p3_t2_color);

		_qgp::D11Vertex3D* vx;
		PrimitiveBegin(QGTPG_LINE_STRIP, count + 1, sizeof(_qgp::D11Vertex3D), (kpointer*)&vx);

		if (colors)
		{
			if (coords)
			{
				for (i = 0; i < count; i++)
					vx[i].Set(pts[i], coords[i], colors[i].ToBgra());
			}
			else
			{
				for (i = 0; i < count; i++)
					vx[i].Set(pts[i], colors[i].ToBgra());
			}
		}
		else
		{
			if (coords)
			{
				for (i = 0; i < count; i++)
					vx[i].Set(pts[i], coords[i], 0xFFFFFFFF);
			}
			else
			{
				for (i = 0; i < count; i++)
					vx[i].Set(pts[i], 0xFFFFFFFF);
			}
		}

		PrimitiveEnd();
	}
}

//
void D11Rdh::PrimitiveBegin(qgTopology tpg, kint verts, kint stride, kpointer* data)
{
	RDHNFO_INVOKE();

	if (_cache.pup_tpg != 0)
	{
		kobj_tracet(KLV_ERROR, KERR_INVAL, "cannot begin draw primitive without end.");
		return;
	}

	_cache.pup_tpg = tpg;
	_cache.pup_verts = verts;
	_cache.pup_vertstride = stride;

	*data = _dvb->Enter(verts * stride);
}

//
void D11Rdh::PrimitiveEnd()
{
	RDHNFO_INVOKE();
	RDHNFO_DRAWCALL();
	RDHNFO_VERTICES(_cache.pup_verts);

	D11Buffer* vb = _dvb->Leave();

	_CommitShader();

	_InternalSetBufferVertex(0, vb, _cache.pup_vertstride, 0, false);
	_InternalSetTopology((qgTopology)_cache.pup_tpg);

	_context->Draw(_cache.pup_verts, 0);

	_cache.pup_tpg = 0;
	_cache.pup_verts = 0;
	_cache.pup_vertstride = 0;
}

//
void D11Rdh::PrimitiveBeginIndexed(qgTopology tpg, kint verts, kint vertstride, kpointer* vertdata, kint indices, kint indstride, kpointer* inddata)
{
	RDHNFO_INVOKE();

	if (_cache.pup_tpg != 0)
	{
		kobj_tracet(KLV_ERROR, KERR_INVAL, "cannot begin draw indexed primitive without end.");
		return;
	}

	_cache.pup_tpg = tpg;
	_cache.pup_verts = verts;
	_cache.pup_vertstride = vertstride;
	_cache.pup_indices = indices;
	_cache.pup_indstride = indstride;


	*vertdata = _dvb->Enter(verts * vertstride);
	*inddata = _dib->Enter(indices * indstride);
}

//
void D11Rdh::PrimitiveEndIndexed()
{
	RDHNFO_INVOKE();
	RDHNFO_DRAWCALL();
	RDHNFO_VERTICES(_cache.pup_verts);

	D11Buffer* vb = _dvb->Leave();
	D11Buffer* ib = _dib->Leave();

	_CommitShader();

	_InternalSetBufferVertex(0, vb, _cache.pup_vertstride, 0, false);
	_InternalSetBufferIndex(ib, D11Convert::IndexStrideFormat(_cache.pup_indstride), 0, false);
	_InternalSetTopology((qgTopology)_cache.pup_tpg);

	_context->DrawIndexed(_cache.pup_indices, 0, 0);

	_cache.pup_tpg = 0;
	_cache.pup_verts = 0;
	_cache.pup_vertstride = 0;
	_cache.pup_indices = 0;
	_cache.pup_indstride = 0;

}

//
void D11Rdh::GlyphBegin(qgShader* shd)
{
	RDHNFO_INVOKE();

	k_return_if_fail(!_cache.pup_glyph);

	SetDhsl(qbStaticDhsl<false>::GetPtr());
	SetRasz(qbStaticRasz<QGFILL_SOLID, QGCULL_BACK>::GetPtr());
	SetBlnd(qbStaticBlnd<QGCM_RGBA, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV, QGBLO_ADD, QGBLF_SA, QGBLF_SA_INV>::GetPtr());
	SetSplr(0, qbStaticSplr<>::GetPtr());
	SetShader(shd != NULL ? shd : _extp.sp_glyph, _extp.vl_p4_color);

	_InternalSetTopology(QGTPG_TRI_STRIP);

	_CommitShader();

	_cache.pup_glyph = true;
}

//
void D11Rdh::GlyphDraw(kint x, kint y, kint w, kint h, const tpl::ColorValue& color, qgTexture* tex)
{
	RDHNFO_INVOKE();
	RDHNFO_DRAWCALL();
	RDHNFO_VERTICES(2);

	k_return_if_fail(_cache.pup_glyph);
	k_return_if_ok(color.a < 10);
	k_return_if_fail(tex != NULL);

	//
	kuint bgra = color.ToBgra();

	float fl = -((float)_prsn.t_vp.size.width * 0.5f) + x;
	float ft = ((float)_prsn.t_vp.size.height * 0.5f) - y;
	float fr = fl + w;
	float fb = ft - h;

	//
	SetTexture(0, tex);

	//
	_qgp::D11Vertex2D* vx = (_qgp::D11Vertex2D*)_dvb->Enter(4 * sizeof(_qgp::D11Vertex2D));
	vx[0].Set(fl, ft, 0.0f, 0.0f, bgra);
	vx[1].Set(fr, ft, 1.0f, 0.0f, bgra);
	vx[2].Set(fl, fb, 0.0f, 1.0f, bgra);
	vx[3].Set(fr, fb, 1.0f, 1.0f, bgra);

	D11Buffer* vb = _dvb->Leave();
	_InternalSetBufferVertex(0, vb, sizeof(_qgp::D11Vertex2D), 0, false);

	_context->Draw(4, 0);

}

//
void D11Rdh::GlyphEnd()
{
	RDHNFO_INVOKE();

	k_return_if_fail(_cache.pup_glyph);

	_cache.pup_glyph = false;
}

//
void D11Rdh::_RoamRdoUnload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.rdos.RemoveNode(base);
}

//
void D11Rdh::_RoamLayoutUnload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.vertexlayouts.RemoveNode(base);
}

//
void D11Rdh::_RoamShaderUnload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.shaders.RemoveNode(base);
}

//
void D11Rdh::_RoamTexture2Unload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.texture2s.RemoveNode(base);
}

//
void D11Rdh::_RoamTexture3Unload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.texture3s.RemoveNode(base);
}

//
void D11Rdh::_RoamAnimTextureUnload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.animtextures.RemoveNode(base);
}

//
void D11Rdh::_RoamDepthBufferUnload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.depthbuffers.RemoveNode(base);
}

//
void D11Rdh::_RoamBufferUnload(qgRdo* base)
{
	RDHNFO_INVOKE();
	RDHNFO_DISPOSE();

	if (!_keep.dispose)
		_keep.buffers.Remove(base);
}

//
qgRdo* D11Rdh::_RoamShaderFind(const D11Shader::Key& key)
{
	return (qgRdo*)_keep.shaders.Get(&key);
}

#endif	// _QG_USE_D11
